﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Demo
{
    public interface IWebHostBuilder
    {
        IWebHost Build();

        IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices);
    }

    public class WebHostBuilder : IWebHostBuilder
    {
        private readonly List<Action<IServiceCollection>> _configureServicesDelegates;

        public WebHostBuilder()
        {
            _configureServicesDelegates = new List<Action<IServiceCollection>>();
        }
        public IWebHost Build()
        {
            var hostingServices = BuildCommonServices();
            var applicationServices = hostingServices.Clone();
            var hostingServiceProvider = hostingServices.BuildServiceProvider();

            var host = new WebHost(applicationServices, hostingServiceProvider);

            host.Initialize();

            return host;
        }
        private IServiceCollection BuildCommonServices()
        {
            var services = new ServiceCollection();

            services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
            services.AddTransient<IFeatureCollection, FeatureCollection>();
            services.AddTransient<IWebHost, WebHost>();
            services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
            foreach (var configureServices in _configureServicesDelegates)
            {
                configureServices(services);
            }

            return services;
        }

        public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
        {
            _configureServicesDelegates.Add(configureServices);
            return this;
        }
    }

    internal static class ServiceCollectionExtensions
    {
        public static IServiceCollection Clone(this IServiceCollection serviceCollection)
        {
            IServiceCollection clone = new ServiceCollection();
            foreach (var service in serviceCollection)
            {
                clone.Add(service);
            }
            return clone;
        }
    }

    public static class WebHostBuilderExtensions
    {
        public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action<IApplicationBuilder> configureApp)
        {
            if (configureApp == null)
            {
                throw new ArgumentNullException(nameof(configureApp));
            }

            var startupAssemblyName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name;

            return hostBuilder
            .ConfigureServices(services =>
            {
                services.AddSingleton<IStartup>(sp =>
                {
                    return new DelegateStartup(sp.GetRequiredService<IServiceProviderFactory<IServiceCollection>>(), configureApp);
                });
            });
        }
    }
}
